/**
 * @file alarm.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include <stdio.h>
#include "lv_watch.h"

#if USE_LV_WATCH_ALARM

/*********************
 *      DEFINES
 *********************/
#define SECDAY              86400L                                           // seconds per day
#define ALARM_TEST  0

static app_adaptor_alarm_t alarms[NV_ALARM_MAX_ALARM_NUM];
static int8_t alarm_recent_index = -1;  // recent alarm index
static app_adaptor_alarm_ind alarm_ind_func = NULL;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void poweron_by_alarm(void);
static void alarm_prepare_destory(lv_obj_t * activity_obj);
static lv_alarm_obj_ext_t * alarm_get_ext(void);
static void alarm_timeout(void * para);
static void alarm_ring(void);
static void alarm_set_alarm(app_adaptor_alarm_t * alarm, uint8_t count);
static void alarm_set_recent_to_rtc(app_adaptor_alarm_t * alarms);
void alarm_audio_ctrl_callback(AUDIO_CTRL_PRIORITY priority);

/**********************
 *   GLOBAL FUNCTIONS
***********************/
lv_obj_t * alarm_create(lv_obj_t * activity_obj)
{
    /* activity obj */
    if(NULL == activity_obj) {
        lv_watch_activity_ext_t activity_ext;
        memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
        activity_ext.actId = ACT_ID_ALARM;
        activity_ext.create = alarm_create;
        activity_ext.prepare_destory = alarm_prepare_destory;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
    }

    /* watch obj */
    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    lv_watch_obj_set_anim_mode(obj, LV_WATCH_ANIM_HOR_RIGHT_HIDE);
    lv_alarm_obj_ext_t * ext = lv_obj_allocate_ext_attr(obj, sizeof(lv_alarm_obj_ext_t));

    /* gif open */
    ext->gif_info = gif_open(obj, ANIM_ALARM, 0);
    if(!ext->gif_info) {
        printf("open anim_alarm gif failed.\n");
        return(NULL);
    }

    /* start timer (1 min) */
    ext->timer_id = Hal_Timer_Start(1000 * 60, alarm_timeout, NULL, 0);
    ext->timer_running = true;

    watch_set_suspend_enable(false, ACT_ID_ALARM, 0);

    return obj;
}

void alarm_init(void)
{
    Hal_Mem_Set(alarms, 0, sizeof(app_adaptor_alarm_t) * NV_ALARM_MAX_ALARM_NUM);
    alarm_recent_index = -1;

#if ALARM_TEST
    app_adaptor_alarm_t app_alarm[NV_ALARM_MAX_ALARM_NUM];
    Hal_Mem_Set(app_alarm, 0, sizeof(app_adaptor_alarm_t) * NV_ALARM_MAX_ALARM_NUM);

    app_alarm[0].valid = 1;
    app_alarm[0].on_off = 1;
    app_alarm[0].hour = 17;
    app_alarm[0].min = 20;
    app_alarm[0].repeat_bitmap = 1;

    app_alarm[1].valid = 1;
    app_alarm[1].on_off = 0;
    app_alarm[1].hour = 15;
    app_alarm[1].min = 13;
    app_alarm[1].repeat_bitmap = 1;

    alarm_set_alarm(app_alarm, 2);

    Hal_Timer_Start(1000 * 5, (CB_FUNC)alarm_ring, NULL, 0);
    //Hal_Timer_Start(1000 * 5, (CB_FUNC)shutdown, NULL, 0);
#else
    nv_watch_alarm_t nv_alarm;
    Hal_Mem_Set(&nv_alarm, 0, sizeof(nv_watch_alarm_t));

    /* read NVM */
    UI_NV_Read_Req(NV_SECTION_UI_ALARM, 0, sizeof(nv_watch_alarm_t), (uint8_t *)&nv_alarm);
    for(uint8_t i = 0; i < NV_ALARM_MAX_ALARM_NUM; i++) {
        alarms[i].valid = nv_alarm.alarm_info[i].valid;
        alarms[i].on_off = nv_alarm.alarm_info[i].on_off;
        alarms[i].hour = nv_alarm.alarm_info[i].hour;
        alarms[i].min = nv_alarm.alarm_info[i].min;
        alarms[i].repeat_bitmap = nv_alarm.alarm_info[i].repeat_bitmap;
    }

    /* set recent alarm to rtc */
    alarm_set_recent_to_rtc(alarms);
#endif

    /* bind set alarm function */
    app_adaptor_set_alarm_bind(alarm_set_alarm);
}

void alarm_cleanup(void)
{
    /* del obj */
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_ALARM);

    if(activity_obj) {
        alarm_prepare_destory(activity_obj);
        lv_obj_del(activity_obj);
    }
}

void alarm_shutdown(hal_rtc_t * poweron_time)
{
    /* set poweron by alarm */
    if(poweron_time) {
        Hal_Rtc_Set_Alarm(RTC_ALARM_1, poweron_time, poweron_by_alarm);
        Hal_Rtc_Enable_Alarm(RTC_ALARM_1, true);
    } else {
        Hal_Rtc_Enable_Alarm(RTC_ALARM_1, false);
    }

    /* shutdown */
    shutdown_confirm_btn_action(NULL, LV_EVENT_CLICKED);
}

void alarm_set_alarm_by_ui(app_adaptor_alarm_t * alarm, uint8_t count)
{
    alarm_set_alarm(alarm, count);

    //indicate alarm to APP
    if(alarm_ind_func) {
        alarm_ind_func(alarm, count);
    }
}

app_adaptor_alarm_t * alarm_get_alarm(void)
{
    uint32_t size = sizeof(app_adaptor_alarm_t) * NV_ALARM_MAX_ALARM_NUM;
    app_adaptor_alarm_t *alarm = (app_adaptor_alarm_t *)Hal_Mem_Alloc(size);
    memcpy(alarm, alarms, size);

    return alarm;
}

void app_adaptor_alarm_ind_bind(app_adaptor_alarm_ind func)
{
    alarm_ind_func = func;
}

void alarm_set_rtc_alarm(void)
{
    /* set recent alarm to rtc */
    alarm_set_recent_to_rtc(alarms);
}

/**********************
*   STATIC FUNCTIONS
**********************/
static void poweron_by_alarm(void)
{
    printf("poweron_by_alarm\n");
}

static void alarm_prepare_destory(lv_obj_t * activity_obj)
{
    lv_watch_png_cache_all_free();

    watch_set_suspend_enable(true, ACT_ID_ALARM, 0);

    /* stop playing tone */
    Hal_Tone_Play_End();

    lv_alarm_obj_ext_t * ext = alarm_get_ext();
    if(ext) {
        /* gif close */
        if(ext->gif_info) {
            gif_close(ext->gif_info);
            ext->gif_info = NULL;
        }

        /* stop timer */
        if(ext->timer_running && ext->timer_id) {
            Hal_Timer_Stop(ext->timer_id);
        }
    }

    Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_3);
#if 0
    /* inform voice msg of the end */
    voice_msg_alarm_end();
#endif
}

static lv_alarm_obj_ext_t * alarm_get_ext(void)
{
    lv_obj_t * activity_obj = NULL;
    lv_obj_t * watch_obj = NULL;
    lv_alarm_obj_ext_t  * ext = NULL;

    activity_obj = lv_watch_get_activity_obj(ACT_ID_ALARM);
    if(activity_obj) {
        lv_watch_get_child_obj(activity_obj, lv_watch_obj_signal, &watch_obj);
    }

    if(watch_obj) {
        ext = lv_obj_get_ext_attr(watch_obj);
    }
    return ext;
}

static void alarm_timeout(void * para)
{
    (void) para;

    lv_alarm_obj_ext_t * ext = alarm_get_ext();
    if(ext) {
        ext->timer_running = false;
        ext->timer_id = NULL;
    }

    alarm_cleanup();
}

void alarm_audio_ctrl_callback(AUDIO_CTRL_PRIORITY priority)
{
    printf("%s,priority is %d\n", __FUNCTION__,priority);
    if(AUDIO_CTRL_PRIORITY_3 == priority)
    {
        lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_ALARM);
        if(activity_obj != NULL) return;

        /* play tone start */
        Hal_NFFS_File_Play_Repeat(AUDIO_ALARM, query_current_volume());

        watch_wakeup_lcd(true);
        /* display alarm animation */
        alarm_create(NULL);
    }
    else if(AUDIO_CTRL_PRIORITY_3 > priority)
    {
        // clean up alarm if any
        alarm_cleanup();
    }
}

static void alarm_ring(void)
{
    printf("%s\n", __FUNCTION__);
    bool alarm_changed = false;

    if(alarm_recent_index >= 0) {
        /* set alarm off */
        for(uint8_t i = 0; i < NV_ALARM_MAX_ALARM_NUM; i++) {
            if(alarm_recent_index == i) {
                if(0 == (alarms[i].repeat_bitmap & 0x80)) {  // alarm once
                    alarms[i].on_off = 0;
                    alarm_changed = true;
                }
            } else {
                if((alarms[i].hour == alarms[alarm_recent_index].hour)
                        && (alarms[i].min == alarms[alarm_recent_index].min)
                        && (0 == (alarms[i].repeat_bitmap & 0x80)) // alarm once
                        && (1 == alarms[i].on_off)) {
                    alarms[i].on_off = 0;
                    alarm_changed = true;
                }
            }
        }

        if(alarm_changed) {
            /* write to NVM */
            nv_watch_alarm_t nv_alarm;
            for(uint8_t i = 0; i < NV_ALARM_MAX_ALARM_NUM; i++) {
                nv_alarm.alarm_info[i].valid = alarms[i].valid;
                nv_alarm.alarm_info[i].on_off = alarms[i].on_off;
                nv_alarm.alarm_info[i].hour = alarms[i].hour;
                nv_alarm.alarm_info[i].min = alarms[i].min;
                nv_alarm.alarm_info[i].repeat_bitmap = alarms[i].repeat_bitmap;
            }

            UI_NV_Write_Req(NV_SECTION_UI_ALARM, 0, sizeof(nv_watch_alarm_t), (uint8_t *)&nv_alarm);
        }

        alarm_recent_index = -1;
    }

    /* set recent alarm to rtc */
    alarm_set_recent_to_rtc(alarms);

    lv_obj_t * alarm_obj = lv_watch_get_activity_obj(ACT_ID_ALARM);
    if(NULL != alarm_obj) {
        return;
    }

    if(!watch_is_ready()) {
        watch_set_alarm_flag(true);
        return;
    }

#if 0
    lv_obj_t * phone_obj = lv_watch_get_activity_obj(ACT_ID_PHONE);
    lv_obj_t * voip_obj = lv_watch_get_activity_obj(ACT_ID_VOIPPHONE);
    if((NULL == phone_obj) && (NULL == voip_obj)) {
        speech_recog_alarm_ind();
        voice_msg_alarm_ind();

        /* play tone start */
        Hal_NFFS_File_Play_Repeat(AUDIO_ALARM, query_current_volume());

        /* display alarm animation */
        alarm_create(NULL);
    } else {
        if(phone_obj) phone_alarm_ind();
        if(voip_obj) voip_alarm_ind();
    }
#endif
    if(AUDIO_CTRL_PRIORITY_3 == Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY_3,alarm_audio_ctrl_callback))
    {
        /* play tone start */
        Hal_NFFS_File_Play_Repeat(AUDIO_ALARM, query_current_volume());

        watch_wakeup_lcd(true);
        /* display alarm animation */
        alarm_create(NULL);
    }

#if USE_LV_WATCH_ALARM_SET
    if(alarm_changed) {
        /* delete alarm add */
        lv_obj_t * alarm_set_obj = lv_watch_get_activity_obj(ACT_ID_ALARM_ADD);
        if(alarm_set_obj) {
            lv_obj_del(alarm_set_obj);
        }

        /* delete alarm edit */
        alarm_set_obj = lv_watch_get_activity_obj(ACT_ID_ALARM_EDIT);
        if(alarm_set_obj) {
            lv_obj_del(alarm_set_obj);
        }

        /* recreate alarm list */
        alarm_set_obj = lv_watch_get_activity_obj(ACT_ID_ALARM_LIST);
        if(alarm_set_obj) {
            alarm_list_create(alarm_set_obj);
        }
    }
#endif
}

static void alarm_set_alarm(app_adaptor_alarm_t * alarm, uint8_t count)
{
    Hal_Mem_Set(alarms, 0, sizeof(app_adaptor_alarm_t) * NV_ALARM_MAX_ALARM_NUM);

    for(uint8_t i = 0; i < count; i++) {
        Hal_Mem_Copy(&alarms[i], alarm + i, sizeof(app_adaptor_alarm_t));
    }

    /* write to NVM */
    nv_watch_alarm_t nv_alarm;
    Hal_Mem_Set(&nv_alarm, 0, sizeof(nv_watch_alarm_t));

    for(uint8_t i = 0; i < NV_ALARM_MAX_ALARM_NUM; i++) {
        nv_alarm.alarm_info[i].valid = alarms[i].valid;
        nv_alarm.alarm_info[i].on_off = alarms[i].on_off;
        nv_alarm.alarm_info[i].hour = alarms[i].hour;
        nv_alarm.alarm_info[i].min = alarms[i].min;
        nv_alarm.alarm_info[i].repeat_bitmap = alarms[i].repeat_bitmap;
    }

    UI_NV_Write_Req(NV_SECTION_UI_ALARM, 0, sizeof(nv_watch_alarm_t), (UINT8 *)&nv_alarm);

    /* set recent alarm to rtc */
    alarm_set_recent_to_rtc(alarms);
}

/**
 * Set recent alarm to rtc
 * param (in) alarms: app_adaptor_alarm_t *
 * return  VOID
 */
static void alarm_set_recent_to_rtc(app_adaptor_alarm_t * alarm)
{
    hal_rtc_t rtc_curr;            // current rtc time
    hal_rtc_t rtc_alarm;           // alarm rtc time
    hal_rtc_t rtc_recent;          // recent alarm rtc time
    uint32_t  second_curr   = 0;   // seconds of current time
    uint32_t  second_recent = 0;   // seconds of recent alarm
    uint32_t  second_tmp    = 0;
    uint32_t  second_alarm[NV_ALARM_MAX_ALARM_NUM];   // second of alarms
    bool has_alarm_on = false;

    Hal_Mem_Set(second_alarm, 0, sizeof(uint32_t) * NV_ALARM_MAX_ALARM_NUM);

    Hal_Rtc_Gettime(&rtc_curr);
    second_curr = time_to_seconds(&rtc_curr);
    rtc_calc_weekday(&rtc_curr);

    /* get recent alarm */
    for(uint8_t i = 0; i < NV_ALARM_MAX_ALARM_NUM; i++) {
        if((1 == alarm[i].valid) && (1 == alarm[i].on_off)) {
            has_alarm_on = true;
            Hal_Mem_Set(&rtc_alarm, 0, sizeof(hal_rtc_t));
            rtc_alarm.tm_sec  = 0;
            rtc_alarm.tm_min  = alarm[i].min;
            rtc_alarm.tm_hour = alarm[i].hour;
            rtc_alarm.tm_mday = rtc_curr.tm_mday;
            rtc_alarm.tm_mon  = rtc_curr.tm_mon;
            rtc_alarm.tm_year = rtc_curr.tm_year;

            if(0 == (alarm[i].repeat_bitmap & 0x80)) {  // alarm once
                second_alarm[i] = time_to_seconds(&rtc_alarm);
                if(second_alarm[i] <= second_curr) {
                    second_alarm[i] += SECDAY; // add one day
                }
            } else { // alarm repeat
                second_alarm[i]    = time_to_seconds(&rtc_alarm);
                second_recent = 0;

                for(uint8_t j = 0; j < 7; j++) {
                    if(0x01 == ((alarm[i].repeat_bitmap >> j) & 0x01)) {
                        second_tmp = second_alarm[i] + ((j - rtc_curr.tm_wday + 7) % 7) * SECDAY;
                        if(second_tmp <= second_curr) {
                            second_tmp += SECDAY * 7; // add one week
                        }

                        if((0 == second_recent) || (second_tmp < second_recent)) {
                            second_recent = second_tmp;
                        }
                    }
                }
                second_alarm[i] = second_recent;
            }
        }
    }

    if(has_alarm_on) {
        second_recent = 0;
        for(uint8_t i = 0; i < NV_ALARM_MAX_ALARM_NUM; i++) {
            if((second_alarm[i] > 0) && ((0 == second_recent) || (second_alarm[i] < second_recent))) {
                second_recent = second_alarm[i];
                alarm_recent_index = i;
            }
        }
    } else {
        alarm_recent_index = -1;
    }

    /* set alarm to rtc */
    if(alarm_recent_index >= 0) {
        seconds_to_time(second_recent, &rtc_recent);

        printf("alarm_set_recent_to_rtc: %02d/%02d %02d:%02d\n",
               rtc_recent.tm_mon,
               rtc_recent.tm_mday,
               rtc_recent.tm_hour,
               rtc_recent.tm_min);

        Hal_Rtc_Set_Alarm(RTC_ALARM_1, &rtc_recent, alarm_ring);
        Hal_Rtc_Enable_Alarm(RTC_ALARM_1, true);
    } else {
        Hal_Rtc_Enable_Alarm(RTC_ALARM_1, false);
    }
}

#endif /*USE_LV_WATCH_ALARM*/
